#include <libgen.h>
#include <unistd.h>
#include "ParserUtil.h"
#include <iostream>
#include "all.h"
#include "Ast.h"
#include <stdio.h>
#include "SymTabMgr.h"
#include "STEClasses.h"
#include "SymTab.h"
#include "Value.h"

using namespace std;


#include "E--.tab.h"
//#define TEST_AST

#define CPP_PROG_NAME "CPP" // Environment variable to specify location of cpp
#define DEFAULT_CPP_PROG_NAME "cpp"
#define CC_PROG_NAME "CC" // Environment variable to specify C compiler
#define DEFAULT_CC_PROG_NAME "gcc -g"
#define CC_PROG_OPTS "CFLAGS"

#define MAX_DEBUG_LEVEL 2

#ifdef DEBUG_LEXER
#ifdef LEXER_OUT
ofstream lexOutStr(LEXER_OUT);
ostream& lexOut = lexOutStr;
#else
ostream& lexOut = cout;
#endif
#endif
FunctionEntry* print;
extern EventEntry* any;
extern int yyparse();
extern int yylinenum;
extern const char* yyfilename;
extern YYSTYPE yylval;
int unused_reg();
string State2Reg(char *str);
int cal_hash(char *str);
string reg_array[1024];
//#define TEST_CODE
//#define TEST_OPTIM
void yyerror(const char *s)
{
  errMsg(s);
}

SymTabMgr stm;
string outputFile;
const char* inputFile = "";
string cppShellCmd, ccShellCmd;

int debugLevel;
bool genCCode;
bool genBinCode;
bool genSharedLib;
bool genDottyCode;
bool genPrologCode;
string outFileSuffix;  
extern FILE* yyin;

vector<three_addr_ins*> inter_code;
vector<string> *state_list;
map<string,string> temp_reg;
int block_count = 0;
bool flag_type = false;
bool flag_code = false;
bool flag_opti = false;

class RegInfo
{
public:
	bool used;
	list<address*> addrlist_; 
};

RegInfo reg_file[1001];

void
printUsage(const char* cmd) {
  cerr << "Usage: " << cmd << //" [-v <num>] "
	"[-I include_dir] [-L<library directory>]\n   [-l<library>]"
    "[-Dmacro[=defn]] [-Uname] " //[-t <target language>]\n   "
    "[-o <output file>] <input file>\n" 
    /*"\t-v: debugging\n"
    "\t\t0: none\n"
    "\t\t1: print AST 'out' file\n"
    "\t\t2: print lots of internal debugging info\n"
    "\t-o output file name\n"
	"\t-t: Specify type of output\n"
	"\t\tc: Output C code\n"
	"\t\tb: Output a binary (.o) file\n"
	"\t\ts: Output a shared library (default)\n"
	"\t\td: Output a dotty file\n"
	"\t\tp: Output Prolog code\n"*/
	"Environment variable CPP can be used to override the default command\n"
    "for running cpp program. This variable can include the program name\n"
    "as well as command-line arguments to the command. Similarly, the\n"
    "environment variable CC can specify the command to be run for compiling\n"
    "C code produced by this compiler, but the options to the compiler can\n"
    "specified independently using the environment variable CFLAGS.\n";
}

int 
parseOptions(int argc, char* argv[]) {
  int c;
  opterr = 0; // Suppress printing of errors by getopt.
  int num_t_options=0;

  if (getenv(CPP_PROG_NAME) == NULL)
	cppShellCmd = DEFAULT_CPP_PROG_NAME;  else cppShellCmd = getenv(CPP_PROG_NAME);
  cppShellCmd += " ";

  if (getenv(CC_PROG_NAME) == NULL)
	ccShellCmd = DEFAULT_CC_PROG_NAME;
  else ccShellCmd = getenv(CC_PROG_NAME);
  ccShellCmd += " ";
  if (getenv(CC_PROG_OPTS) != NULL) {
	ccShellCmd += getenv(CC_PROG_OPTS);
	ccShellCmd += " ";
  }

  while (1) {
	if (argc != 3) {
		cerr << "Please specify only a single input file\n";
		printf("Usage(): ./demo [option] [file]\n"
			"\toptions = -t:typecheck, -c:codegen, -o:optimized codegen\n");	
		return -1;
	}
	else 
	{
		if (!strcmp("-t", argv[1])) {
			flag_type = true;	
		}
		else if (!strcmp("-c", argv[1])) {
			flag_code = true;	
		}
		else if (!strcmp("-o", argv[1])) {
			flag_type = false;	
			flag_code = true;
			flag_opti = true;	
		}
		else {
			printf("Usage(): -t typecheck, -c codegen, -o optimized codegen\n");	
		}
		
		inputFile = argv[2];
	  	return 0;
	
	}
  }

  genSharedLib = !(genCCode || genBinCode || genDottyCode || genPrologCode);
  if (genSharedLib)
	outFileSuffix = ".so";

  if (*inputFile == '\0') 
    return -1;

  if (outputFile == "") {
	size_t pos;
	if (genSharedLib) {
	  string sinputFile(inputFile);
	  if ((pos = sinputFile.rfind('/')) == string::npos)
		outputFile = "lib" + sinputFile;
	  else outputFile = 
			 sinputFile.substr(0, pos) + "/lib" + sinputFile.substr(pos+1);
	}
	else outputFile = inputFile;

	if ((pos = outputFile.rfind('.')) != outputFile.npos)
	  outputFile.replace(pos, outputFile.size(), outFileSuffix);
	else outputFile += outFileSuffix;
  }

  return 0;
}
void printAddr(address* addr, ostream& os, int indent)
{
	if(addr != NULL)
	{
	switch(addr->atype)
	{
	case address::CONST_BOOL:
		os<<addr->bValue;
		break;
	case address::CONST_INT:
		os<<addr->iValue;
		break;
	case address::CONST_DOUBLE:
		os<<double(addr->dValue);
		break;
	case address::VARI:
		os<<addr->ve->name();
		break;
	case address::TEMP:
		os<<addr->temp;
		break;
	case address::TEMP_FLOAT:
		os<<addr->temp;
		break;
	case address::REG:
		os<<addr->reg;
		break;
	case address::FREG:
		os<<addr->reg;
        case address::CONST_STR:
                //william                
                if (addr->sValue == "IN")
                    os<<"R999";
                else if (addr->sValue.substr(0,5) == "State")
                    os<<State2Reg((char *)addr->sValue.c_str());
                else
                    os<<addr->sValue; //origin
		break;
	case address::CVARI:
		os<<addr->cve->name();
		break;
	}
	}
	os<<" ";
}

//---------------------------------------------------------------------//

void setBlock()
{
	vector<three_addr_ins*>::iterator iter;

	for(iter = inter_code.begin(); iter != inter_code.end(); iter++)
	{

		if((*iter)->arity == three_addr_ins::LABEL)
			block_count++;
		(*iter)->block = block_count;
	}
}

void const_propogation(){
	vector<three_addr_ins*>::iterator iter;

	for(iter = inter_code.begin(); iter != inter_code.end(); iter++)
	{

		if((*iter)->op=="PRINTI"||(*iter)->op=="PRINTS"|| (*iter)->op=="PRINTF") continue ;

		switch ((*iter)->arity){

		case 1:
                        if((*iter)->op != "RETURN"){
			if((*iter)->oprand1){
			if((*iter)->oprand1->atype==address::CONST_INT){
				if((*iter)->dest && (*iter)->dest->ve){
				(*iter)->dest->ve->const_int=(*iter)->oprand1->iValue;
				(*iter)->dest->ve->set_const(1);
				}
			}
			else if((*iter)->oprand1->atype==address::CONST_DOUBLE){
				if((*iter)->dest){
				(*iter)->dest->ve->const_double=(*iter)->oprand1->dValue;
				(*iter)->dest->ve->set_const(2);
				}
			}

			if((*iter)->oprand1->atype==address::VARI){
				if((*iter)->oprand1){
				if((*iter)->oprand1->ve->is_const()==1){
					(*iter)->oprand1->iValue=(*iter)->oprand1->ve->const_int;
					(*iter)->oprand1->atype=address::CONST_INT;

				}

				else if((*iter)->oprand1->ve->is_const()==2){

					(*iter)->oprand1->dValue=(*iter)->oprand1->ve->const_double;
					(*iter)->oprand1->atype=address::CONST_DOUBLE;
				}
				}
			}
			}}
			break ;

		case 2:

			if((*iter)->oprand1){
			if((*iter)->oprand1->atype==address::VARI){

					if((*iter)->oprand1->ve->is_const()==1){
						(*iter)->oprand1->iValue=(*iter)->oprand1->ve->const_int;
						(*iter)->oprand1->atype=address::CONST_INT;
					}

					else if((*iter)->oprand1->ve->is_const()==2){
						(*iter)->oprand1->dValue=(*iter)->oprand1->ve->const_double;
						(*iter)->oprand1->atype=address::CONST_DOUBLE;
					}
			}

			if((*iter)->oprand2){
			if((*iter)->oprand2->atype==address::VARI){

				if((*iter)->oprand2->ve->is_const()==1){
						(*iter)->oprand2->iValue=(*iter)->oprand2->ve->const_int;
						(*iter)->oprand2->atype=address::CONST_INT;
				}

				else if((*iter)->oprand2->ve->is_const()==2){
						(*iter)->oprand2->dValue=(*iter)->oprand2->ve->const_double;
						(*iter)->oprand2->atype=address::CONST_DOUBLE;
				}
			}
			}
			}
			break;

		default:
			break;
		}//end swith
	}

}

void deadCode()
{
	vector<three_addr_ins*>::iterator iter;
	for(iter = inter_code.begin(); iter != inter_code.end(); iter++)
	{

		switch ((*iter)->arity){

		case 1:
			if((*iter)->op == "MOVI" || (*iter)->op == "MOVF" || (*iter)->op == "MOVIF" || (*iter)->op == "MOVFI" || (*iter)-> op == "MOVS"|| (*iter)-> op == "MOVL"){

				if((*iter)->oprand1->atype==address::VARI){
                	(*iter)->oprand1->ve->increase_right_ref_count();
                }

                if((*iter)->dest->atype == address::VARI)
                {
                	vector<three_addr_ins*>::iterator last,newIt;

                	if((*iter)->dest->ve->has_defined()&&(*iter)->dest->ve->get_right_ref_count()==0){
                		//remove last defination
                		last = (*iter)->dest->ve->get_last_def();

                		if((*last)->oprand1->atype==address::VARI){
                			(*last)->oprand1->ve->decrease_right_ref_count();
                		}

                		if((*last)->oprand2&&(*last)->oprand2->atype==address::VARI){
                		    (*last)->oprand2->ve->decrease_right_ref_count();
                		}

                		int n = iter - last;
                		newIt = inter_code.erase( last );
                		iter = newIt + n -1 ;
                	}

                	else{  //first defined here
                		(*iter)->dest->ve->init_ref_count();
                		(*iter)->dest->ve->set_def(iter);
                		(*iter)->dest->ve->set_defined();
                	}
                }
			}
			break;

		case 2:

			if((*iter)->oprand1->atype==address::VARI)
			   (*iter)->oprand1->ve->increase_right_ref_count();

			if((*iter)->oprand2->atype==address::VARI)
			   (*iter)->oprand2->ve->increase_right_ref_count();

			if((*iter)->dest->atype == address::VARI)
			{
				vector<three_addr_ins*>::iterator last,newIt;

				if((*iter)->dest->ve->has_defined()&&(*iter)->dest->ve->get_right_ref_count()==0){
					//remove last defination
					last = (*iter)->dest->ve->get_last_def();

					if((*last)->oprand1->atype==address::VARI){
						(*last)->oprand1->ve->decrease_right_ref_count();
					}

					if((*last)->oprand2&&(*last)->oprand2->atype==address::VARI){
						(*last)->oprand2->ve->decrease_right_ref_count();
					}

					int n = iter - last;
					newIt = inter_code.erase( last );
					iter = newIt + n -1 ;

				}

				else{  //first defined here

					(*iter)->dest->ve->init_ref_count();
					(*iter)->dest->ve->set_def(iter);
					(*iter)->dest->ve->set_defined();
				}
			}

			break;

		default:
			break;
		}//end switch

	}
}
/*
 * peehole optimization:
 * optimize the intermediate code by merging two consecutive
 * three-address codes into one.
 * e.g. :
 * 		ADD 3 x y
 *
 *
 */
void removeDouble(){

	vector<three_addr_ins*>::iterator iter , iter2;
	for(iter = inter_code.begin(); iter != inter_code.end(); iter++)
	{
		if((*iter)->arity == three_addr_ins::BINARY){
			iter2 = iter+1 ;

			if((*iter)->op=="ADD" ){
				if((*iter2)->op=="MOVI"){

					address* temp = (*iter2)->dest;
					(*iter)->dest =temp;
					inter_code.erase(iter2);

				}
			}

			if((*iter)->op=="FADD"){
				if((*iter2)->op=="MOVF"){

					address* temp = (*iter2)->dest;
					(*iter)->dest =temp;
					inter_code.erase(iter2);

				}
			}
		}
	}
}

void optimization(){
	setBlock();
	removeDouble();
	deadCode();
	const_propogation();
}

//william
int cal_hash(char *str) {
    unsigned int hash = 0;
    int c;
    while ((c = *(str++)))
        hash += c;

    return hash % 1024;
}
//william
string State2Reg(char *str) {
    int hashv = cal_hash(str);
    //cout<<"---------"<<hashv<<endl;
    if (reg_array[hashv].size() == 0) {
        int num = unused_reg();
        reg_file[num].used = true;
        string tmp;
        for(int i = 0; i < 3 - strlen(itoa(num).c_str()); i++)
			tmp += "0";
        reg_array[hashv] = "R" + tmp + itoa(num);
        //cout<<"---------"<<num<<endl;
    }
    return reg_array[hashv];   
}

void printInst(three_addr_ins* ins, ostream & os, int indent)
{
   /* if(ins->dest)
	cout<<ins->dest->atype<<":";*/
   /*if(ins->oprand1)	
	cout<<ins->oprand1->atype<<": ";*/
    switch(ins->arity) {
        case 1:
        	if(ins->label)
            os<<ins->op<<" ";
            printAddr(ins->oprand1,os,indent);
	    if(ins->op == "MOVI" || ins->op == "MOVF" || ins->op == "MOVIF" || ins->op == "MOVFI" || ins-> op == "MOVS" || ins->op =="NEG"|| ins-> op == "MOVL")
	    printAddr(ins->dest,os,indent);
            os<<endl;
            break;
        case 2:
            os<<ins->op<<" ";
            printAddr(ins->oprand1,os,indent);
            printAddr(ins->oprand2,os,indent);
            printAddr(ins->dest,os,indent);
            os<<endl;
            break;

        case three_addr_ins::LABEL:
            os<<ins->label<<":"<<endl;
            break;
        case three_addr_ins::GOTOLABEL:
            if(('A'<=ins->label[0] && ins->label[0]<='Z') ||('a'<=ins->label[0] && ins->label[0]<='z'))
                if (ins->label[0] == 'S') //william: State
                    os<<"JMPI "<<State2Reg(ins->label)<<endl;
                else
                    os<<"JMP "<<ins->label<<endl;
            break;        
        case three_addr_ins::IFLABEL:
            os<<"if ";
            printAddr(ins->oprand1, os, indent);
            os<<ins->op;
            printAddr(ins->oprand2, os, indent);
            os<<" JMP "<< ins->label<<endl;
            break;
	case three_addr_ins::FUN_LABEL:
	    os<<ins->label<<":"<<endl;
            os<<"ADD R900 1 R901"<<endl;
	    os<<"LDI R901 R500"<<endl;
	    break;	
        case three_addr_ins::RULELABEL:
            os<<"if IN==";
            printAddr(ins->oprand1, os, indent);
            os<<" goto "<< ins->label<<endl;
            break;
        case three_addr_ins::STATELABEL:
            os<<"if State=="<<ins->state<<" goto "<<ins->label<<endl;
            break;
	case three_addr_ins::REALPARAM:
	    os<<"REALPARAM ";
	    printAddr(ins->oprand1, os, indent);
	    os<<endl;
	    break;
	case three_addr_ins::CALL:
	    os<<"CALL ";
	    printAddr(ins->oprand1,os,indent);
	    os<<endl;
	    break;
        case three_addr_ins::DFA_HEADER:
	    //
	    break;
        default:
            os<<" ------ undefined arity --------- "<<endl;
            return;
        break;
    }    
    return;
}

int unused_reg()
{
	for(int i = 0; i <= 999 ; i++)
	{
		if(reg_file[i].used == false && i != 900 && i != 901)
		{
			reg_file[i].used = true;
			return i;
		}
	}
}

void setreg(address * oprand,int num)
{
	if(oprand->atype == address::VARI)	
	{
	if(oprand->ve->type()->tag() != Type::DOUBLE)			
		oprand->reg = "R";
	else
		oprand->reg = "F";
	}
	else if(oprand->atype != address::CONST_DOUBLE && oprand -> atype != address::TEMP_FLOAT)
		oprand -> reg = "R";
	else
		oprand -> reg = "F";
	
	for(int i = 0; i < 3 - strlen((itoa(num)).c_str()); i++)
		oprand->reg += "0";
	
	oprand->reg += itoa(num);
	oprand -> atype = address::REG;
}

void getReg(three_addr_ins* ins)
{
	//if(ins->oprand1)	
	//cout<<ins->oprand1->atype<<": ";
        if (ins->arity == 3 || ins->arity ==  three_addr_ins::LABEL || ins->arity == three_addr_ins::GOTOLABEL|| ins->arity == three_addr_ins::FUN_LABEL) //william
        {
		printInst(ins,cout,0);
		return;
	}
	else if(ins-> op == "RETURN" && !ins -> oprand1)
	{
		cout<<"JMPI R500"<<endl;
		return;
	}
	
	 if (ins->arity == three_addr_ins::DFA_HEADER) { //DFA header
            cout<<"LOOP:"<<endl;
            cout<<"IN R999"<<endl;
            cout<<"PRTS \"IN=\""<<endl;
            cout<<"PRTI R999"<<endl;            
            cout<<"JMPC EQ R999 0 END"<<endl;
            return;
        }
        if(ins->oprand1->atype == address::VARI)
	{
		if(ins->oprand1->ve->get_reg() == -1)		
		{
			
			ins->oprand1->ve->set_reg(unused_reg());
			
			setreg(ins->oprand1,ins->oprand1->ve->get_reg());
			
			/*if(ins->op != "RPARAM")
			{
				cout <<"ADD "<<"R900 "<<ins->oprand1->ve->offSet()<<" "<<"R901"<<endl;
				cout <<"LDI "<<"R901 "<<ins->oprand1->reg<<endl;
			}*/
		}
		else
		{
			setreg(ins->oprand1,ins->oprand1->ve->get_reg());
		}
		
		if(ins->oprand1->ve->type()->tag() != Type::DOUBLE  && ins->dest && ins->dest->atype == address::TEMP_FLOAT)
		{
			int num = unused_reg();
			string freg = "F";			
			for(int i = 0; i < 3 - strlen((itoa(num)).c_str()); i++)
				freg += "0";
			freg += itoa(num);
			cout<<"MOVIF "<<ins->oprand1->reg<<" "<<freg<<endl;
			ins->oprand1->reg = freg;
			ins->oprand1->atype = address::FREG;
			
		}
	}
	else if(ins->oprand1->atype == address::TEMP || ins->oprand1->atype == address::TEMP_FLOAT)
	{
		
		ins->oprand1->reg = temp_reg.find(ins->oprand1->temp)->second;
		//cout<<ins->oprand1->reg<<endl;
		
		if(ins->oprand1->atype == address::TEMP && ins->oprand1->reg[0]!= 'F' && ins->dest->ve && ins->dest->ve->type()->tag() == Type::DOUBLE)
		{
			int num = unused_reg();
			string freg = "F";			
			for(int i = 0; i < 3 - strlen((itoa(num)).c_str()); i++)
				freg += "0";
			freg += itoa(num);
			cout<<"MOVIF "<<ins->oprand1->reg<<" "<<freg<<endl;
			ins->oprand1->reg = freg;
			ins->oprand1->atype = address::FREG;
		}
		ins->oprand1->atype = address::REG;
	}
	else if(ins->oprand1->atype == address::CONST_INT && ins->op != "PARAM" && ins->dest && ins->dest->atype == address::TEMP_FLOAT)
		{
		
			int num = unused_reg();
			string freg = "F";			
			for(int i = 0; i < 3 - strlen((itoa(num)).c_str()); i++)
				freg += "0";
			freg += itoa(num);	
			cout <<"MOVIF "<<ins->oprand1->iValue<<" "<<freg<<endl;
			ins->oprand1->reg = freg;
			ins->oprand1->atype = address::FREG;
		}		
	
	if(ins->oprand1->atype == address::REG && ins->oprand1->reg[0]!= 'F'&& ins->op != "MOVIF" && ins->dest && ins->dest->atype == address::VARI && ins->dest->ve->type()->tag() == Type::DOUBLE)
	{
			int num = unused_reg();
			string freg = "F";			
			for(int i = 0; i < 3 - strlen((itoa(num)).c_str()); i++)
				freg += "0";
			freg += itoa(num);
			cout<<"MOVIF "<<ins->oprand1->reg<<" "<<freg<<endl;
			ins->oprand1->reg = freg;
			ins->oprand1->atype = address::FREG;
			
	}


	if(ins->arity == 2 || ins->arity == three_addr_ins::IFLABEL)
	{
		if(ins->oprand2->atype == address::VARI )
		{
			if(ins->oprand2->ve->get_reg() == -1)
			{
				
				ins->oprand2->ve->set_reg(unused_reg());
			
				setreg(ins->oprand2,ins->oprand2->ve->get_reg());
				/*if(ins->op != "RPARAM")				
				{
					cout <<"ADD "<<"R900 "<<ins->oprand2->ve->offSet()<<" "<<"R901"<<endl;
					cout <<"LDI "<<"R901 "<<ins->oprand2->reg<<endl;
				}*/
			}
			else
			{
				
				setreg(ins->oprand2,ins->oprand2->ve->get_reg());
			}
			if(ins->oprand2->ve->type()->tag() != Type::DOUBLE &&  ins->dest && ins->dest->atype == address::TEMP_FLOAT)
			{
				int num = unused_reg();
				string freg = "F";			
				for(int i = 0; i < 3 - strlen((itoa(num)).c_str()); i++)
					freg += "0";
				freg += itoa(num);
				cout<<"MOVIF "<<ins->oprand2->reg<<" "<<freg<<endl;
				ins->oprand2->reg = freg;
				ins->oprand2->atype = address::FREG;
			}
		}
		else if(ins->oprand2->atype == address::TEMP  || ins->oprand2->atype == address::TEMP_FLOAT)
		{
			ins->oprand2->atype = address::REG;
			ins->oprand2->reg = temp_reg.find(ins->oprand2->temp)->second;
		}
		
		else if(ins->oprand2->atype == address::CONST_INT && ins->op != "PARAM" && ins->dest && ins->dest->atype == address::TEMP_FLOAT)
		{
		
		int num = unused_reg();
		string freg = "F";			
		for(int i = 0; i < 3 - strlen((itoa(num)).c_str()); i++)
			freg += "0";
		freg += itoa(num);	
		cout <<"MOVIF "<<ins->oprand2->iValue<<" "<<freg<<endl;
		ins->oprand2->reg = freg;
		ins->oprand2->atype = address::FREG;
		}
		if(ins->oprand2->atype == address::REG && ins->oprand2->reg[0]!= 'F' && ins->dest && ins->dest->atype == address::VARI && ins->dest->ve->type()->tag() == Type::DOUBLE)
	{
			int num = unused_reg();
			string freg = "F";			
			for(int i = 0; i < 3 - strlen((itoa(num)).c_str()); i++)
				freg += "0";
			freg += itoa(num);
			cout<<"MOVIF "<<ins->oprand2->reg<<" "<<freg<<endl;
			ins->oprand2->reg = freg;
			ins->oprand2->atype = address::FREG;
			
	}
	
	}
	if(ins->dest)
	{	
	if(ins->dest->atype == address::VARI)
	{
		
		if(ins->dest->ve->get_reg() == -1)		
		{
			

			ins->dest->ve->set_reg(unused_reg());
			setreg(ins->dest,ins->dest->ve->get_reg());
		}
		else 
		{
			setreg(ins->dest,ins->dest->ve->get_reg());
		}
		
	}
	else if	(ins->dest->atype == address::TEMP || ins->dest->atype == address::TEMP_FLOAT)
	{
		
		
		int num = unused_reg();
		
		setreg(ins->dest,num);
		temp_reg.insert(make_pair(ins->dest->temp,ins->dest->reg));
	}
	}
	if(ins->op == "PARAM")
	{
		
		if(ins->oprand1->atype == address::CONST_STR)
		{		
			cout<<"MOVS ";
			printAddr(ins->oprand1,cout,0);
			int num = unused_reg();
		
			string reg = "R";			
			for(int i = 0; i < 3 - strlen((itoa(num)).c_str()); i++)
				reg += "0";
			reg += itoa(num);
			cout<<reg<<endl;
			
			cout<<"STI "<<reg;
			//printAddr(ins->oprand1,cout,0);
			cout<<" R900"<<endl;
			cout<<"SUB R900 1 R900"<<endl;
		}
		else
		{
			if((ins->oprand1->ve && ins->oprand1->ve->type()->tag() == Type::DOUBLE) || ins->oprand1->atype == address::CONST_DOUBLE)
			cout<<"STF ";
			else
			cout<<"STI ";
			printAddr(ins->oprand1,cout,0);
			cout<<" R900"<<endl;
			cout<<"SUB R900 1 R900"<<endl;
		}
	}
	else if(ins->op == "CALL")
	{
		string reg = "R";
		int num = unused_reg();
		string label = newLabel();
		for(int i = 0; i < 3 - strlen((itoa(num)).c_str()); i++)
		reg += "0";
		reg += itoa(num);
		cout<<"MOVL "<<label<<" "<<reg<<endl;
		cout<<"STI "<<reg<<" R900"<<endl;
		cout<<"SUB R900 1 R900"<<endl;
		cout<<"JMP "<<ins->oprand1->reg<<endl;
		cout<<label<<":"<<endl;
		if(ins->oprand1->fe->type()->retType()->tag() != Type::VOID)
		{
			//cout<<"SUB R900 2 R901 "<<endl;
			cout<<"LDI R900 ";
			printAddr(ins->dest,cout,0);
			cout<<endl;
		}
	}
	else if(ins->op == "RPARAM")
	{
		cout<<"ADD "<<"R901 1 R901"<<endl;
		if(ins->oprand1->ve->type()->tag() != Type::DOUBLE)		
		cout<<"LDI R901 ";
		else
		cout<<"LDF R901 ";
		printAddr(ins->oprand1,cout,0);
		cout<<endl;
	}
	else if(ins-> op == "FRPARAM")
	{
	//	cout<<"ADD "<<"R900 1 R901"<<endl;
	//	cout<<"LDI "<<"R901 R500"<<endl;
		cout<<"ADD "<<"R901 1 R901"<<endl;
		if(ins->oprand1->ve->type()->tag() != Type::DOUBLE)		
		cout<<"LDI R901 ";
		else
		cout<<"LDF R901 ";
		printAddr(ins->oprand1,cout,0);
		cout<<endl;
	}
	else if(ins-> op == "RETURN")
	{
		//cout<<"SUB R900 2 R901"<<endl;
		
		if((ins->oprand1->ve && ins->oprand1->ve->type()->tag() == Type::DOUBLE) || ins->oprand1->atype == address::CONST_DOUBLE)
		cout<<"STF ";
		else
		cout<<"STI ";

		printAddr(ins->oprand1,cout,0);
		cout<<" R900"<<endl;
		cout<<"JMPI R500"<<endl;
	}
	else if(ins->arity == three_addr_ins::IFLABEL)
	{
		address* temp;
		if(ins->op == "<")
		{
			ins->op = "GT";
			temp = ins->oprand1;
			ins->oprand1 = ins->oprand2;
			ins->oprand2 = temp;
		}		
		else if(ins->op == "<=")
		{
			ins->op = "GE";
			temp = ins->oprand1;
			ins->oprand1 = ins->oprand2;
			ins->oprand2 = temp;
		}
		else if(ins->op == "==")
		{
			ins->op = "EQ";
		}
		else if(ins->op == "!=")
		{
			ins->op = "NE";
		}
		else if(ins->op == ">=")
		{
			ins->op = "GE";
		}
		else if(ins->op == ">")
		{
			ins->op = "GT";
		}
		cout<<"JMPC ";
                cout<<" "<<ins->op<<" ";
		printAddr(ins->oprand1,cout,0);		
		printAddr(ins->oprand2,cout,0);
		cout<<ins->label<<endl;
	}
      
	else	
	printInst(ins,cout,0);
}


int 
main(int argc, char *argv[], char *envp[]) {

  string ccCmd;
  int optionsOK = parseOptions(argc, argv);
  if (optionsOK < 0)
	return -1;

  cppShellCmd += inputFile;
  cppShellCmd += " ";

  /* ccCmd = "mv ";
  ccCmd += outputFile;
  ccCmd += " ";
  ccCmd += outputFile;
  ccCmd += ".c; ";
  ccCmd += ccShellCmd;
  if (genSharedLib)
	ccCmd += "-shared ";
  ccCmd += "-o ";
  ccCmd += outputFile;*/

  if ((yyin = popen(cppShellCmd.c_str(), "r")) == NULL) {
    cerr << "Unexpected error in reading input file\n";
    return 1;
  }

#ifdef TEST_LEXER
  int token;

  while (token = yylex()) {
	if (token == TOK_SYNTAX_ERROR) {
	  cout << yyfilename << ":" << yylinenum 
		   << ": Error: Unrecognized token `" << yytext << "'\n";
	}
	else {
      cout << "Token: " << token << " ";
      switch(token)
        {
        case TOK_UINTNUM:
          cout << "Attribute: (int) " << yylval.uVal;
          break;

        case TOK_DOUBLENUM:
          cout << "Attribute: (float) " << yylval.dVal;
          break;

        case TOK_STRCONST:
          cout << "Attribute: (string) " << yylval.cVal;
          break;

        case TOK_ID:
          cout << "Attribute: (id) " << yylval.cVal;
          break;

        default:
          break;
        }
      cout << endl;
	}
  }

  return 0;
#else
  //   yydebug = 1;
  any = new EventEntry("any");
  if (stm.insert(any) == OK) {
    Type *te = new Type((vector<Type*>*)NULL, Type::EVENT);
    any->type(te);
  }

  print = new FunctionEntry("print");
  stm.insert(print);
	
  yyparse();
  stm.leaveToScope(SymTabEntry::GLOBAL_KIND);
  GlobalEntry *ge = (GlobalEntry*)(stm.currentScope());
  if (ge != NULL) {
#ifdef TEST_AST
	cout << "Finished parsing, here is the AST\n";
	//ge->print(cout, 0);
#endif
	if (flag_type) {
		ge->typeCheck();
		ge->typePrint(cout, 0);
	}

        //ge->genCode(cout,0);
	ge->genCode3(cout,0);

        if (flag_opti)
		optimization();
	
if (flag_code) {
        vector<three_addr_ins*>::iterator iter;

       /* for(iter = inter_code.begin(); iter != inter_code.end(); iter++)
	{
		printInst(*iter,cout,0);
		//cout<<(*iter)->op;
	}
	cout<<endl<<endl;*/
	bool first = true;
	for(iter = inter_code.begin(); iter != inter_code.end(); iter++)
	{
            if((*iter)->arity == three_addr_ins::FUN_LABEL && first) {
		cout<<"JMP LOOP"<<endl;
	    	first = false;
	    }
	    getReg(*iter);
	    if((*iter)->op == "PRTI" || (*iter)->op == "PRTF" || (*iter)->op == "PRTS")
	    {
			cout<<"PRTS \"\\n\""<<endl;
		}
    }
  
  //william: DFA footer
        
        cout<<"JMP LOOP"<<endl;
        cout<<"END:" <<endl;

        cout<<"PRTS \" DONE \""<<endl;
	/*for(iter = inter_code.begin(); iter != inter_code.end(); iter++)
	{
		printInst(*iter,cout,0);
	}*/
   
	//ge->genCode(cout,0);
	//ge->genCode(cout, 0);
}
  }
#endif
}
